<template>
  <div class="app">
    <div v-if="isShowModal" class="modal">
      <div class="wrap">
        <span @click="hideModal" class="close">×</span>
        <div class="title">接受{{ modalInfo.name }}呼叫将中断本次通话</div>
        <div class="btns">
          <button @click="handleNo(modalInfo)" class="off">挂断</button>
          <button @click="handleOk(modalInfo)" class="on">接受</button>
        </div>
      </div>
    </div>
    <audio class="audio" loop controls ref="a1" src="../assets/weixin.mp3" />
    <div class="container">
      <div class="main">
        <div class="debug" style="display: none">
          devStatus:{{ devStatus }}<br />
          timerMap: {{ timerMap }}
          <br />
          status:{{ status }}
          <br />
          modalInfo: {{ modalInfo }}
          {{ newCallStatus }}
          <br />
          curUserInfo.userId: {{ curUserInfo.userId }}
          <br />
          deptName: {{ deptName }}
          <br />
          deptId: {{ deptId }}
          <br />
          roomId: {{ roomId }}
          <br />
          userId: {{ userId }}
          <hr />
          {{ curStoreInfo }}
          <hr />
          sdkAppId: {{ sdkAppId }}
          <br />
          <br />
          userSig{{ userSig }}
          <br />
        </div>
        <div class="remoteBox">
          <div v-for="item in remoteStreamArr" :key="item" :id="'remote_stream_' + item"></div>
        </div>
        <div v-if="showStoreList" class="statusList">
          <ul>
            <li v-for="item in storeList" :key="item.id">
              <span class="text">{{ item.name }}</span>
              <span v-if="item.status === 1" class="zt">
                <i class="green"></i>
                空闲
              </span>
              <span v-else class="zt">
                <i class="red"></i>
                忙碌
              </span>
            </li>
          </ul>
        </div>
        <div class="remoteTitle">
          <div v-if="curStoreInfo" class="groupItem">
            <span class="dot green"></span>
            <span @click="toggleStoreList" class="text">{{ curStoreInfo.name }}</span>
            <span class="arrow" :class="{ active: showStoreList }"></span>
          </div>
        </div>
      </div>

      <div class="side">
        <div v-if="showCallList" class="fromCallList">
          <ul>
            <li v-for="item in callList" :key="item.id">
              <span class="text2">{{ item.name }}</span>
              <span class="zt">
                <a @click="fnReceiveCall(item)" class="call" href="javascript:;"></a>
                <a @click="fnHangUpNoAnswerCall(item)" class="offcall" href="javascript:;"></a>
              </span>
            </li>
          </ul>
        </div>
        <div class="text">
          <div v-if="latestCall" class="fromCall">
            <!-- <span @click="turnOn(latestCall)" class="item">{{latestCall.name}}来电呼叫</span> -->
            <span @click="toggleCallList" class="item">{{ latestCall.name }}来电呼叫</span>
            <span v-if="callNum" @click="toggleCallList" class="icon" :class="{ revert: showCallList }"></span>
          </div>
          <span v-if="callNum" class="fromCallNum">{{ callNum }}</span>
        </div>
        <div class="video">
          <!-- <div v-if="newCallStatus && callNum > 0" id="newCall">
            <div>
              <img />
              <div>
                <a @click="turnOn(latestCall)" class="call" href="javascript:;">接通</a>
                <a @click="hangUp(latestCall)" class="offcall" href="javascript:;">挂断</a>
              </div>
            </div>
          </div>-->
          <div id="local_stream"></div>
          <Person
            style="position: absolute; left: 0; top: 0"
            v-show="!status"
            :width="width"
            :height="height"
            ref="person"
          />
        </div>
        <div class="title">{{ deptName }}</div>
        <div class="btns">
          <!-- <button @click="join">加入</button> -->
          <!-- <div @click="handleBtnCall" v-show="btnCall" class="call"></div> -->
          <div @click="fnHangUpCall(roomId)" v-show="btnOff" class="off"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import TRTC from 'trtc-js-sdk'
import config from '@/config'
import api from '@/config/api'
import ReconnectingWebSocket from 'reconnecting-websocket'
import Person from '@/components/Animate/Person'

import { checkSystemRequirements, checkDevice } from '@/utils/check'

import '@/assets/css/common.less'

export default {
  components: {
    Person,
  },
  data() {
    return {
      devStatus: false, // 设备可用状态
      // 动画的宽高，根据分辨率来适配
      // 253px  353px  1024
      // 388px  398px  1920
      width: '',
      height: '',
      sid: this.$route.query.sid, // 路由参数sid，获取token用
      isShowModal: false, // 是否显示弹层
      modalInfo: '', // 弹层显示的门店信息
      curStoreInfo: null, // 对方门店信息，默认中控，如果角色是中控，则不显示
      status: 0, // 当前用户状态 0空闲，1忙碌

      showStoreList: false,
      showCallList: false,
      callList: [], //来电列表
      storeList: [], //门店列表

      latestCall: null, // 最近来电
      btnCall: false, // 拨打按钮是否显示
      btnOff: false, // 挂断按钮是否显示

      // ======================= userInfo
      curUserInfo: {}, // 当用户信息，初始化获取
      deptId: '',
      deptName: '',
      roomId: '',
      newCallStatus: true,

      // ======================= RTC
      sdkAppId: '',
      userId: '',
      userSig: '',
      mode: 'rtc',

      client: null,
      localStream: null,
      remoteStreamArr: [],
      videoProfile: '720p',

      inited: false,
      isJoined: false,
      accessToken: '',
      timer: null, // 定时器，关闭通知铃声
      timeout: 30000, // 30秒未接听关闭声音
      timerMap: {}, // 定时器
    }
  },
  computed: {
    callNum() {
      return this.callList.length
    },
    storeNum() {
      return this.storeList.length
    },
  },
  async mounted() {
    const SysRequired = await checkSystemRequirements()

    this.deviceChangeFn()

    if (SysRequired) {
      const deviceStatus = await checkDevice()
      if (!deviceStatus.ok) {
        this.devStatus = false
        this.$toast({ message: deviceStatus.msg, duration: 5000 })
      } else {
        this.devStatus = true
      }
    } else {
      this.$toast({ message: '该浏览器不支持视频通话', duration: 5000 })
    }

    // 初始化数据
    this.initData()
    // 初始化状态
    this.dataInit()
    this.logger()
  },
  methods: {
    // 初始化数据
    async initData() {
      await this.getUserInfo()
      let protocol = location.protocol === 'http' ? 'ws' : 'wss'
      // dev
      const url = `${protocol}://${location.host}/socketServiceUrl/video/connect?userId=${this.curUserInfo.userId}`

      // prod
      // const url = `${protocol}://${location.host}/video/connect?userId=${this.curUserInfo.userId}`
      console.log(url)

      this.rws = new ReconnectingWebSocket(url, [], {
        debug: true,
      })

      this.bindSocket()

      if (window.screen.width > 1024) {
        this.width = '388px'
        this.height = '398px'
      } else {
        this.width = '253px'
        this.height = '353px'
      }
      this.$refs.person.start()

      // 更新房间列表
      await this.getRoomList()
    },

    // 初始化状态
    dataInit() {
      this.curStoreInfo = null
      this.storeList = []
      this.btnCall = false
      this.btnOff = false
    },

    // 监听设备是否连接
    deviceChangeFn() {
      const _this = this
      navigator.mediaDevices.ondevicechange = async function () {
        const deviceStatus = await checkDevice()
        if (!deviceStatus.ok) {
          _this.$toast({ message: deviceStatus.msg, duration: 5000 })
          _this.devStatus = false
          _this.noticeVideo(6, 1, this.deptId)
        } else {
          _this.devStatus = true
          _this.$toast({ message: '设备已连接', duration: 5000 })
        }
      }
    },
    // 绑定socket
    bindSocket() {
      this.rws.onopen = () => {
        this.rws.send('中控连接到视频通话...')
        console.log('中控连接到视频通话...')
      }

      this.rws.onmessage = async (evt) => {
        this.handleMessage(evt)
      }

      this.rws.onclose = () => {
        this.rws.send('中控断开连接...')
        console.log('中控断开连接...')
        // this.rws.close()
      }

      this.rws.onerror = (evt) => {
        console.log('error', evt)
      }
    },
    // socket消息触发
    async handleMessage(evt) {
      const obj = JSON.parse(evt.data)
      const { id, roomId, type } = JSON.parse(evt.data)
      console.log('!!!!!!!!!!!!!!!!!!!!!!中控收到信息', obj)

      // type 1呼叫，2关闭，3触发门店端按钮，4接听电话通知其他中控刷新列表

      // 第一个来电，并且中控还未接听状态，铃声定时器
      if (this.status === 0 && type === 1) {
        this.audioStop()
        this.$refs.a1.play()
        this.timer = setTimeout(() => {
          this.audioStop()
        }, this.timeout)
      }

      // 30秒未接听自动挂断
      if (type === 1) {
        this.autoOffCall(id, roomId)
        // console.log(id, roomId)
      }

      // 收到门店挂断的通知
      if (type === 2) {
        // 更新视频通话列表
        console.log('socket type=2挂断', this.timerMap[roomId])        
        // 清除定时器
        this.clearAutoTimer(roomId)
        // 停止音乐
        this.audioStop()
        console.log('清空了定时器')
      }
      
      // 正在通话中
      if (type === 5) {
        console.log('socket type=5通话中', this.timerMap[roomId])
        this.clearAutoTimer(roomId)
        this.audioStop()
        console.log('清空了定时器')
      }


      

      // 更新房间列表
      await this.getRoomList()
    },
    // 自动挂断30秒未接听的来电
    autoOffCall(id, roomId) {
      // 如果30秒内，接听了，取消挂断
      
      console.log('##################自动挂断清除默认定时器')
      this.clearAutoTimer(roomId)
      this.timerMap[roomId] = setTimeout(() => {
        // 请求更新房间状态接口
        // 更新房间
        console.log(id, roomId)
        console.log('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^超时自动关闭房间')
        this.clearAutoTimer(roomId)

        // 真实挂断
        // api
        console.log('###################超时调API接口挂断')
        this.hangupCall(roomId)
        this.callList = this.callList.filter((call) => call.id !== id)
      }, this.timeout)
      console.log('##################开启自动挂断默认定时器', this.timerMap[roomId], roomId)
    },

    // API: 通知接口 sendId需要统一，platform为1时，传deptId
    async noticeVideo(type, platform, sendId, roomId) {
      // type	    是	Integer	1 呼叫 2 关闭 3 按钮通话 4 按钮拨打中 5 通话中 6 设备异常
      // platform	是	Integer	推送目标 1门店 2中控
      // sendId	  是	String	推送目标id 可以为deptId 可以为userId
      // roomId	  否	String	挂断需要传(房间号)
      try {
        const res = await this.$axios.post(
          api.sysNoticeVideo,
          {
            type,
            platform,
            sendId,
            roomId,
          },
          {
            headers: {
              Authorization: `Bearer ${this.accessToken}`,
            },
            interceptors: {
              // 为false 时，不再经过某个interceptor
              request: false,
              response: false,
              error: false,
            },
            postDataType: 'json',
          }
        )
      } catch (error) {
        console.log(error)
        this.$toast({ message: '网络错误，请联系管理员', duration: 5000 })
      }
    },
    // API: 获取状态房间状态
    async getStatus(id) {
      try {
        const res = await this.$axios.get(api.getStatus, {
          params: {
            id,
          },
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
          interceptors: {
            // 为false 时，不再经过某个interceptor
            request: false,
            response: false,
            error: false,
          },
        })
        const { data } = res.data
        return data
      } catch (e) {
        console.log('获取状态信息失败')
        this.$toast({ message: '获取状态信息失败', duration: 5000 })
        return -1
      }
    },

    // API: 获取用户信息
    async getUserInfo() {
      if (!this.sid) {
        return
      }
      try {
        const res = await this.$axios.get(api.getAdminLoginInfo, {
          params: {
            sid: this.sid,
          },
          interceptors: {
            // 为false 时，不再经过某个interceptor
            request: false,
            response: false,
            error: false,
          },
        })
        const data = res.data
        // token授权
        if (data.status === 0) {
          const { accessToken, refreshToken } = data.data
          this.$store.commit('REFRESH_USER', { accessToken, refreshToken })
          this.curUserInfo = data.data
          this.userId = data.data.userCode
          this.deptId = data.data.deptId
          this.deptName = data.data.departmentName
          this.accessToken = accessToken
        }
      } catch (e) {
        console.log('获取用户信息失败，请重新登录')
        this.$toast({ message: '获取用户信息失败，请重新登录', duration: 5000 })
      }
    },
    // API: 获取房间列表
    async getRoomList() {
      try {
        const res = await this.$axios.get(api.videoList, {
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
          interceptors: {
            // 为false 时，不再经过某个interceptor
            request: false,
            response: false,
            error: false,
          },
        })
        const data = res.data
        // this.callList = data
        if (data.data.length > 0) {
          this.callList = data.data
          // let temp = {}
          // let result = []
          // this.callList.forEach((item, index) => {
          //   if (!temp[item.id]) {
          //     result.push(item)
          //     temp[item.id] = true
          //   }
          // })

          // this.callList = result
        } else {
          this.callList = []
        }
      } catch (e) {
        console.log('获取房间列表信息失败')
        this.$toast({ message: '获取房间列表信息失败', duration: 5000 })
      }
    },
    // API: 更新房间状态
    async updateStatus(id, roomId) {
      if (!id) {
        return
      }
      try {
        const res = await this.$axios.get(api.updateStatus, {
          params: {
            id,
            roomId,
          },
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
          interceptors: {
            // 为false 时，不再经过某个interceptor
            request: false,
            response: false,
            error: false,
          },
        })
      } catch (e) {
        console.log(e)
      }
    },
    // API: 挂断来电
    async hangupCall(roomId) {
      if (!roomId) {
        return
      }
      try {
        const res = await this.$axios.get(api.closeVideoRoomAdmin, {
          params: {
            roomId,
          },
          headers: {
            Authorization: `Bearer ${this.accessToken}`,
          },
          interceptors: {
            // 为false 时，不再经过某个interceptor
            request: false,
            response: false,
            error: false,
          },
        })
      } catch (e) {
        console.log(e)
      }
    },

    // 获取来电列表，并切换
    async toggleCallList() {
      // if (this.callNum === 0) {
      //   return
      // }
      await this.getRoomList()

      if (this.callList.length > 0) {
        this.showCallList = !this.showCallList
      }
    },
    // 显示当前登录账号的名称
    toggleStoreList() {
      if (this.storeNum === 0) {
        return
      }
      this.showStoreList = !this.showStoreList
    },
    // 隐藏弹层
    hideModal() {
      this.isShowModal = false
    },
    // 显示弹层
    showModal() {
      this.isShowModal = true
    },
    // 关闭来电音频
    audioStop() {
      clearTimeout(this.timer)
      this.timer = null
      this.$refs.a1.currentTime = 0
      this.$refs.a1.pause()
    },

    // 弹层取消按钮
    handleNo(item) {
      // 挂断未接通的电话
      this.fnHangUpNoAnswerCall(item)
      this.isShowModal = false
    },

    // 弹层确定按钮
    async handleOk(item) {
      // this.curStoreInfo = {...item}
      // 挂断当前的
      // console.log(item)
      await this.fnHangUpCall(this.roomId)
      console.log('挂断电话............')
      this.fnReceiveCall(item)
      this.isShowModal = false
      this.clearAutoTimer(item.roomId)
    },

    // 接听来电，根据当前来电的状态来做处理，空闲状态时：正常接听，忙碌状态时：显示弹层，来做后续操作（接听，挂断）
    async fnReceiveCall(item) {
      // 加入房间
      // 更新状态
      // 获取curStoreInfo
      // 更改自身状态
      if (this.status === 0) {
        // 空闲状态
        this.receiveCall(item)
      } else if (this.status === 1) {
        // 忙碌状态
        this.modalInfo = { ...item }
        this.isShowModal = true
      }

      console.log('fnReceiveCall:', this.status)
    },

    // 清除自动挂断定时器
    clearAutoTimer(roomId){
      console.log('清除timer：', this.timerMap[roomId])
      clearTimeout(this.timerMap[roomId])
      this.$set(this.timerMap, roomId, null)
      console.log('###################',this.timerMap)
    },

    // 接听电话
    async receiveCall(item) {
      

      if (!this.devStatus) {
        this.$toast({ message: '请检查设备是否正常', duration: 5000 })
        return
      }
      console.log('接听电话，关闭定时器##############')
      this.clearAutoTimer(item.roomId)

      //状态(00发起通话 01通话中 02会员挂断 03通话结束)
      const data = await this.getStatus(item.id)

      if (data === 0) {
        console.log('可以接听电话.....')
        this.roomId = item.roomId
        await this.join()
        this.curStoreInfo = { ...item }
        this.callList = this.callList.filter((call) => call.id !== item.id)
        await this.updateStatus(item.id, item.roomId)
        this.fnReceiveCallStatus()
        this.rws.send(`中控接听了来电，id:${item.id}, roomID：${item.roomId}`)
        
      } else {
        alert('当前视频通话已被其他客服接听')
        await this.getRoomList()
      }
    },
    // 接听电话后更改状态
    fnReceiveCallStatus() {
      this.status = 1
      this.btnOff = true
      this.showCallList = false
      this.audioStop()
    },
    // 挂断还未接听的电话
    async fnHangUpNoAnswerCall(item) {
      // api
      await this.noticeVideo(2, 1, this.deptId, item.roomId)
      await this.hangupCall(item.roomId)
      this.callList = this.callList.filter((call) => call.id !== item.id)
      this.audioStop()
      // 清除定时器      
      this.clearAutoTimer(item.roomId)
    },
    // 挂断正在通话的电话
    async fnHangUpCall(roomId) {
      roomId = roomId || this.roomId
      // api 退出房间
      await this.leave()
      await this.hangupCall(roomId)
      this.fnHangUpCallStatus()
    },
    // 挂断正在通话的来电时，重置数据
    fnHangUpCallStatus() {
      this.status = 0
      this.btnOff = false
      this.curStoreInfo = null
      this.roomId = ''
      this.audioStop()
    },

    // ================= RTC
    // 开启日志
    logger() {
      TRTC.Logger.setLogLevel(TRTC.Logger.LogLevel.DEBUG)
    },

    // 初始化配置，全局只执行一次
    init() {
      if (!this.inited) {
        const { sdkAppId, userSig } = genTestUserSig(this.userId)
        this.sdkAppId = sdkAppId
        this.userSig = userSig

        this.client = TRTC.createClient({
          mode: this.mode,
          sdkAppId: this.sdkAppId,
          userId: this.userId,
          userSig: this.userSig,
        })

        // 绑定事件
        this.bindEvents()
        this.inited = true
      }
    },

    // 创建房间
    async join() {
      return new Promise(async (resolve, reject) => {
        if (this.isJoined) {
          console.log('已经加入房间，不能重复加入房间')
          reject('已经加入房间，不能重复加入房间')
          return
        }

        this.init()

        try {
          await this.client.join({ roomId: this.roomId })
          this.isJoined = true
          console.log('加入房间成功', this.userId)
          resolve()

          // 创建本地流
          this.localStream = TRTC.createStream({
            video: true,
            audio: true,
          })
          this.localStream.setVideoProfile(this.videoProfile)

          // 初始化本地流
          await this.localStream.initialize()
          console.log('初始化本地流成功')

          // 播放
          this.localStream.play('local_stream')
          // 推流
          await this.publish()
        } catch (e) {
          console.log('加入房间失败', e)
          reject('加入房间失败', e)
        }
      })
    },

    // 离开房间
    async leave() {
      if (!this.isJoined) {
        console.warn('leave() - please join() firstly')
        return
      }
      // ensure the local stream is unpublished before leaving.
      await this.unpublish()

      // leave the room
      await this.localStream.stop()
      await this.localStream.close()
      await this.client.leave()
      this.localStream = null
      this.isJoined = false
      this.status = 0
    },

    // 推流
    async publish() {
      if (!this.isJoined) {
        console.warn('publish() - please join() firstly')
        return
      }
      if (this.isPublished) {
        console.warn('duplicate RtcClient.publish() observed')
        return
      }
      try {
        await this.client.publish(this.localStream)
      } catch (e) {
        console.error('failed to publish local stream ' + e)
        this.isPublished = false
      }

      this.isPublished = true
    },

    async unpublish() {
      if (!this.isJoined) {
        console.warn('unpublish() - please join() firstly')
        return
      }
      if (!this.isPublished) {
        console.warn('RtcClient.unpublish() called but not published yet')
        return
      }

      await this.client.unpublish(this.localStream)
      this.isPublished = false
    },

    // 绑定事件
    bindEvents() {
      // fired when a remote stream is added
      this.client.on('stream-added', (evt) => {
        const remoteStream = evt.stream
        const id = remoteStream.getId()
        const userId = remoteStream.getUserId()
        console.log(`remote stream added: [${userId}] ID: ${id} type: ${remoteStream.getType()}`)
        console.log('subscribe to this remote stream')
        this.client.subscribe(remoteStream)
      })
      // fired when a remote stream has been subscribed
      this.client.on('stream-subscribed', (evt) => {
        const remoteStream = evt.stream
        const id = remoteStream.getId()
        this.remoteStreamArr.push(id)

        // this.addView(id)
        this.$nextTick(() => {
          remoteStream.play('remote_stream_' + id + '')
        })

        console.log('stream-subscribed ID: ', id)
      })

      this.client.on('stream-removed', (event) => {
        const remoteStream = event.stream
        const id = remoteStream.getId()
        this.remoteStreamArr = this.remoteStreamArr.filter((id2) => id2 !== id)
      })

      // 远端视频流断开连接，挂断本地流
      this.client.on('peer-leave', (event) => {
        console.log('peer-leave..................')
        this.fnHangUpCall(this.roomId)
      })
    },
  },
  watch: {
    callList(val) {
      if (val.length === 0) {
        this.showCallList = false
      }

      if (val.length >= 1) {
        this.$nextTick(() => {
          this.latestCall = val[0]
        })
      } else {
        this.$nextTick(() => {
          this.latestCall = null
        })
      }
    },
    status(val) {
      if (val === 0) {
        this.newCallStatus = true
      } else {
        this.newCallStatus = false
      }
    },
  },
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->


<style scoped lang="less">

.debug{
  bottom: 0;
}
.test1{
  background-color: #fff;
}
.remoteBox {
  position: absolute;
  top: 96px;
  left: 65px;
  width: 1030px;
  height: 681px;
  background: #0b1333 url('~@/assets/img/logo-text.png') center no-repeat;
}
</style>
